// bzoj2310
// 题意：
// 给定一个n(<=100) * m(<=8)的棋盘，每个格子有一个权值，问随便选择一个起点，
// 得到一条简单路径，使得每个格子最多经过一次，并使得路径权值和最大。
//
// 题解：
// 这就是带独立插头的简单路径模板题。
//
// 很良心的贴一下状态转移(left up, 0是无插头，1是左括号插头，2是右括号插头，3是独立插头)：
// A.当a[x1][y1]不可达，若当前插头为00，则忽略该格，插头仍为00。
// B.
//   1 .当前插头为00。
//        可以新建一对匹配的插头，转移插头为12。
//        若当前独立插头数不超过1，可以新建一个向右或向下的独立插头，转移插头为03和30。
//        可以忽略该格，转移插头为00。
//   2. 当前插头为01或10。
//        如果右侧没有到边界且可达，可以将左括号向右延伸，转移插头为01。
//        如果下面没有到边界且可达，可以将左括号向下延伸，转移插头为10。
//        如果当前独立插头数不超过1，可以将该处的左括号“封住”，
//          路径的左端在这里终止，转移插头为00，同时将与之匹配的右括号修改为独立插头。
//   3. 当前插头为02或20。
//        如果右侧没有到边界且可达，可以将右括号向右延伸，转移插头为02。
//        如果下面没有到边界且可达，可以将右括号向下延伸，转移插头为20。
//        如果当前独立插头数不超过1，可以将该处的右括号“封住”，路径的右端在这里终止，
//          转移插头为00，同时将与之匹配的左括号修改为独立插头。
//   4. 当前插头为03或30。
//        如果右侧没有到边界且可达，可以将独立插头向右延伸，转移插头为03。
//        如果下面没有到边界且可达，可以将独立插头向下延伸，转移插头为30。
//        如果当前仅有这个独立插头，且没有其它匹配的左右插头，说明目前的状态下图中仅有一条路径，可以更新答案。
//   5. 当前插头为11，将两对匹配的括号合并。找到当前向下位置右侧匹配的右括号，修改为左括号，转移插头为00。
//   6. 当前插头为22，将两对匹配的括号合并。找到当前向右位置左侧匹配的左括号，修改为右括号，转移插头为00。
//   7. 当前插头为12，这对插头一定是相互匹配的，不能合并，忽略。
//   8. 当前插头为21，这对插头一定不匹配，直接合并，转移状态为00。
//   9. 当前插头为33，若当前没有其它匹配的左右插头，可以更新答案。
//  10. 当前插头为31或13，可以将两条路径连接，将与左括号匹配的右括号修改为独立插头，转移插头为00。
//  11. 当前插头为32或23，可以将两条路径连接，将与右括号匹配的左括号修改为独立插头，转移插头为00。
//
// run: $exec < input
// opt: 0
// flag: -g
#include <iostream>
#include <iomanip>
#include <algorithm>

#define DEBUG 0

int const  inf = 1 << 28;
int const maxn = 120;
int a[maxn][10];
int ans;

struct hash_table
{
	int const static maxsize = 30000;
	int hash[maxsize];
	int key[maxsize];
	int val[maxsize];
	int count;

	void clear()
	{
		std::fill(key, key + maxsize, -1);
		std::fill(hash, hash + maxsize, 0);
		std::fill(val, val + maxsize, -inf);
		count = 0;
	}

	void alloc_node(int pos, int key_)
	{
		hash[pos] = ++count;
		key[count] = key_;
	}

	int & operator[](int key_)
	{
		int p;
		for (int i = key_ % maxsize; ; i++) {
			if (i == maxsize) i = 0;
			if (!hash[i]) alloc_node(i, key_);
			if (key[hash[i]] == key_) {
				p = hash[i];
				break;
			}
		}
		return val[p];
	}
};

hash_table f[2];
int now, prev;
int n, m;

int get(int st, int x) { return (st >> ((x - 1) << 1)) & 3; }

void change(int & st, int p, int v)
{
	int w = (p - 1) << 1;
	st &= (~0) ^ (3 << w);
	st |= v << w;
}

int find(int st, int p)
{
	for (int i = p, d = 0, step = get(st, p) == 1 ? 1 : -1; ; i += step) {
		int t = get(st, i);
		if (t == 1) d += 1;
		else if (t == 2) d += -1;
		if (!d) return i;
	}
	return -1;
}

int count_alone_plug(int st)
{
	int ret = 0;
	for (int i = 1; i <= m + 1; i++)
		ret += get(st, i) == 3;
	return ret;
}

int count_normal_plug(int st)
{
	int ret = 0;
	for (int i = 1; i <= m + 1; i++)
		ret += get(st, i) == 1 || get(st, i) == 2;
	return ret;
}

void print(int st)
{
	for (int i = 1; i <= m + 1; i++)
		std::cout << get(st, i);
}

void dp(int i, int j)
{
	now ^= 1; prev ^= 1;
	f[now].clear();
	for (int t = 1; t <= f[prev].count; t++) {
		int st = f[prev].key[t];
		int tst = st;
		int v = f[prev].val[t];
		int va = v + a[i][j];
		int left = get(st, j);
		int up = get(st, j + 1);

#if DEBUG
		std::cerr << "(" << i << ", " << j << ") = " << std::setw(2) << v << "  left = " << left << "  up = " << up << "  ";
		print(st);
		std::cerr << "\n";
#endif

		int alone = count_alone_plug(st);
		int normal = count_normal_plug(st);
		if (!left && !up) {
			f[now][st] = std::max(f[now][st], v);
			if (i != n && j != m) {
				change(st, j, 1);
				change(st, j + 1, 2);
				f[now][st] = std::max(f[now][st], va);
			}
			if (alone <= 1) { // FIXME alow two alone plug? seems two is not correct
				if (i != n) {
					st = tst;
					change(st, j, 3);
					change(st, j + 1, 0);
					f[now][st] = std::max(f[now][st], va);
				}
				if (j != m) {
					st = tst;
					change(st, j, 0);
					change(st, j + 1, 3);
					f[now][st] = std::max(f[now][st], va);
				}
			}
		} else if (left == 3 && up == 3) {
			if (alone == 2 && !normal)
				ans = std::max(ans, va);
		} else if (left == 3 && up) {
			change(st, find(st, j + 1), 3);
			change(st, j, 0);
			change(st, j + 1, 0);
			f[now][st] = std::max(f[now][st], va);
		} else if (up == 3 && left) {
			change(st, find(st, j), 3);
			change(st, j, 0);
			change(st, j + 1, 0);
			f[now][st] = std::max(f[now][st], va);
		} else if (left == up) {
			change(st, find(st, j + 2 - left), left);
			change(st, j, 0);
			change(st, j + 1, 0);
			f[now][st] = std::max(f[now][st], va);
		} else if (left == 1 && up == 2) {
		} else if (left == 2 && up == 1) {
			change(st, j, 0);
			change(st, j + 1, 0);
			f[now][st] = std::max(f[now][st], va);
		} else if (left == 3 || up == 3) {
			if (j != m) {
				change(st, j, 0);
				change(st, j + 1, 3);
				f[now][st] = std::max(f[now][st], va);
			}
			if (i != n) {
				st = tst;
				change(st, j, 3);
				change(st, j + 1, 0);
				f[now][st] = std::max(f[now][st], va);
			}
			if (alone == 1 && !normal)
				ans = std::max(ans, v);
		} else if (left) {
			if (i != n) f[now][st] = std::max(f[now][st], va);
			if (j != m) {
				change(st, j, 0);
				change(st, j + 1, left);
				f[now][st] = std::max(f[now][st], va);
			}
			if (alone <= 1) {
				st = tst;
				change(st, find(st, j), 3);
				change(st, j, 0); change(st, j + 1, 0);
				f[now][st] = std::max(f[now][st], v);
			}
		} else if (up) {
			if (j != m) f[now][st] = std::max(f[now][st], va);
			if (i != n) {
				change(st, j + 1, 0);
				change(st, j, up);
				f[now][st] = std::max(f[now][st], va);
			}
			if (alone <= 1) {
				st = tst;
				change(st, find(st, j + 1), 3);
				change(st, j, 0); change(st, j + 1, 0);
				f[now][st] = std::max(f[now][st], v);
			}
		}
	}
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin >> n >> m;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			std::cin >> a[i][j];

	now = 0; prev = 1;
	f[0][0] = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) dp(i, j);
		if (i == n) continue;
		for (int j = 1; j <= f[now].count; j++)
			f[now].key[j] <<= 2;
	}
	std::cout << ans << "\n";
}

